from datetime import datetime

from flask import render_template, redirect, flash, url_for, request
from flask_login import current_user, login_user, logout_user, login_required
from werkzeug.urls import url_parse

from app import app, db  # 从app包中导入 app这个实例
from app.email import send_password_reset_email
from app.forms import LoginForm, RegistrationForm, EditProfileForm, EmptyForm, ResetPasswordRequestForm
from app.models import User


# 2个路由
@app.route('/')
@app.route('/index')
@login_required  # 16.17.7  @login_required decorator, Requiring Users To Login
# 1个视图函数
def index():
    posts = [  # 创建一个列表：帖子。里面元素是两个字典，每个字典里元素还是字典，分别作者、帖子内容。
        {
            'author': {'username': 'John'},
            'body': 'Beautiful day in Portland!'
        },
        {
            'author': {'username': 'Susan'},
            'body': 'The Avengers movie was so cool!'
        }
    ]
    # return render_template('index.html', title='Home', user=current_user, posts=posts)
    # 16.17.8 Do not pass user to template anymore
    return render_template('index.html', title='Home', user=current_user, posts=posts)


# #16.17.5 user login--
# @app.route('/login', methods=['GET', 'POST'])
# def login():
#     # deal with a weired situation, allow this mistake; The value of current_user variable can be a user object from the database
#     # or a special anonymous user object if the user did not log in yet
#     if current_user.is_authenticated:
#         return redirect(url_for('index'))
#     form = LoginForm()
#     if form.validate_on_submit():
#         # In place of the flash() call that I used earlier, now I can log the user in for real.
#         # The first step is to load the user from the database
#         user = User.query.filter_by(username=form.username.data).first()
#         # implement a fake login that just issued a flash() message
#         # check if the password that also came with the form is valid
#         # two possible error conditions: the username can be invalid, or the password can be incorrect for the user.
#         # In either of those cases, I flash an message, and redirect back to the login prompt so that the user can try again.
#         if user is None or not user.check_password(form.password.data):
#             flash('Invalid username or password')
#             return redirect(url_for('login'))
#         login_user(user, remember=form.remember_me.data)
#         return redirect(url_for('index'))
#     return render_template('login.html', title='Sign In', form=form)

# 16.17.7 重定向到 next 页面
@app.route('/login', methods=['GET', 'POST'])
def login():
    # deal with a weired situation, allow this mistake; The value of current_user variable can be a user object from the database
    # or a special anonymous user object if the user did not log in yet
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        # In place of the flash() call that I used earlier, now I can log the user in for real.
        # The first step is to load the user from the database
        user = User.query.filter_by(username=form.username.data).first()
        # implement a fake login that just issued a flash() message
        # check if the password that also came with the form is valid
        # two possible error conditions: the username can be invalid, or the password can be incorrect for the user.
        # In either of those cases, I flash an message, and redirect back to the login prompt so that the user can try again.
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=form.remember_me.data)

        next_page = request.args.get('next')  # 16.17.7 重定向到 next 页面
        if not next_page or url_parse(next_page).netloc != '':
            next_page = url_for('index')
        return redirect(next_page)  # 16.17.7 重定向到 next 页面

    return render_template('login.html', title='Sign In', form=form)


# 16.17.6 user login--logout view function
@app.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('index'))


# 16.17.9 用户注册视图函数
@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:  # 防止已注册用户再次注册
        return redirect(url_for('index'))
    form = RegistrationForm()
    if form.validate_on_submit():  # 处理POST请求
        user = User(username=form.username.data, email=form.email.data)  # 创建用户，设置密码，添加到数据库
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        flash('Congratulations, you are now a registered user!')  # 刷新消息
        return redirect(url_for('login'))  # 引导到登录页面
    return render_template('register.html', title='Register', form=form)  # 处理Get请求


# 1819.1 User Profile Page--User profile view function, dynamic component
@app.route('/user/<username>')
@login_required
def user(username):
    # a variant of first() called first_or_404(), in the case that there are no results automatically sends a 404 error back to the client.
    user = User.query.filter_by(username=username).first_or_404()
    # initialize a fake list of posts for this user
    posts = [
        {'author': user, 'body': 'Test post #1'},
        {'author': user, 'body': 'Test post #2'}
    ]
    # render a new user.html template to which I pass the user object and the list of posts.
    return render_template('user.html', user=user, posts=posts)


# 1819.5  Recording The Last Visit Time For a User
@app.before_request
def before_request():
    if current_user.is_authenticated:
        current_user.last_seen = datetime.utcnow()
        db.session.commit()


# 1819.6 tie form class and template together
@app.route('/edit_profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
    form = EditProfileForm()
    if form.validate_on_submit():
        current_user.username = form.username.data
        current_user.about_me = form.about_me.data
        db.session.commit()
        flash('Your changes have been saved.')
        return redirect(url_for('edit_profile'))
    # 18.19.6 validate_on_submit()返回False情形之一，第一次以GET请求表格时，将存储在用户字段中的数据移动到表单中，呈现编辑用户资料表单
    elif request.method == 'GET':  # 1819.6  GET for the initial request, and POST for a submission that failed validation
        form.username.data = current_user.username
        form.about_me.data = current_user.about_me
    # 18.19.6 validate_on_submit()返回False情形之二，POST请求且验证错误，呈现编辑用户资料表单
    return render_template('edit_profile.html', title='Edit Profile',
                           form=form)

# 202122.7  Follow and unfollow routes.; 注意此处为miguelgrinberg版本
# @app.route('/follow/<username>')
# @login_required
# def follow(username):
#     form = EmptyForm()
#     if form.validate_on_submit():
#         user = User.query.filter_by(username=username).first()
#         # 避免用户在浏览器直接输入地址关注不存在的用户
#         if user is None:
#             flash('User {} not found.'.format(username))
#             return redirect(url_for('index'))
#         # 避免用户在浏览器直接输入地址关注自己
#         if user == current_user:
#             flash('You cannot follow yourself!')
#             return redirect(url_for('user', username=username))
#         current_user.follow(user)
#         db.session.commit()   # 触发数据库变更，在followers表中添加一条记录
#         flash('You are following {}!'.format(username))
#         return redirect(url_for('user', username=username))
#     else:
#         return redirect(url_for('index'))

# 202122.7  Follow and unfollow routes.; 注意此处为豆约翰版本
@app.route('/follow/<username>')
@login_required
def follow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User {} not found.'.format(username))
        return redirect(url_for('index'))
    if user == current_user:
        flash('You cannot follow yourself!')
        return redirect(url_for('user', username=username))
    current_user.follow(user)
    db.session.commit()
    flash('You are following {}!'.format(username))
    return redirect(url_for('user', username=username))




# 202122.7  unfollow routes. 注意此处为miguelgrinberg版本
# @app.route('/unfollow/<username>', methods=['GET','POST'])
# @login_required
# def unfollow(username):
#     form = EmptyForm()
#     if form.validate_on_submit():
#         user = User.query.filter_by(username=username).first()
#         if user is None:
#             flash('User {} not found.'.format(username))
#             return redirect(url_for('index'))
#         if user == current_user:
#             flash('You cannot unfollow yourself!')
#             return redirect(url_for('user', username=username))
#         current_user.unfollow(user)
#         db.session.commit()
#         flash('You are not following {}.'.format(username))
#         return redirect(url_for('user', username=username))
#     else:
#         return redirect(url_for('index'))


# 202122.7  unfollow routes. 注意此处为豆约翰版本
@app.route('/unfollow/<username>')
@login_required
def unfollow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User {} not found.'.format(username))
        return redirect(url_for('index'))
    if user == current_user:
        flash('You cannot unfollow yourself!')
        return redirect(url_for('user', username=username))
    current_user.unfollow(user)
    db.session.commit()
    flash('You are not following {}.'.format(username))
    return redirect(url_for('user', username=username))

# 23.4 重置密码请求的视图函数，注意此处无@login_required
@app.route('/reset_password_request', methods=['GET','POST'])
def reset_password_request():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = ResetPasswordRequestForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data).first()
        if user:
            send_password_reset_email(user)
        flash('Check your email for the instructions to reset your password')
        return redirect(url_for('login'))
    return render_template('reset_password_request.html', title='Reset Password', form=form)

# 23.6  仅用于测试前半部分，不实现其功能，只测试是否能发送邮件
@app.route('/reset_password/<token>', methods=['GET', 'POST'])
def reset_password(token):
    pass